package com.mjk.manager.magicapi.plugin.webservice.wsdl;

import com.mjk.common.tools.logger.BizLogger;
import com.mjk.common.util.StrUtils;
import lombok.Data;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import javax.wsdl.*;
import javax.wsdl.extensions.ExtensibilityElement;
import javax.wsdl.factory.WSDLFactory;
import javax.wsdl.xml.WSDLReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * wsdl 解析为 soap协议
 * wsdl 节点信息
 * Types - 数据类型定义的容器，它使用某种类型系统(一般地使用XML Schema中的类型系统)。
 * Message - 通信消息的数据结构的抽象类型化定义。使用Types所定义的类型来定义整个消息的数据结构。
 * Operation - 对服务中所支持的操作的抽象描述，一般单个Operation描述了一个访问入口的请求/响应消息对。
 * PortType - 对于某个访问入口点类型所支持的操作的抽象集合，这些操作可以由一个或多个服务访问点来支持。
 * Binding - 特定端口类型的具体协议和数据格式规范的绑定。
 * Port - 定义为协议/数据格式绑定与具体Web访问地址组合的单个服务访问点。
 * Service- 相关服务访问点的集合。
 */
public class MagicApiWsdlUtils {
    /**
     * soap 协议模版
     */
    private final static  String SOUP_TEMPLATE = """
            <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:res="%s">
               <soapenv:Header/>
               <soapenv:Body>
                  <res:%s>
                    %s
                  </res:%s>
               </soapenv:Body>
            </soapenv:Envelope>
            """;

    /**
     * 根据wsdl 地址获取 soap 协议
     * @return
     */
    public static String getSoapProtocol(String wsdl){
        SoapModel model = getSoapModel(wsdl);
        StringBuilder args = new StringBuilder();
        for(String str : model.getArgs()){
            args.append("<"+str+"><![CDATA[?]]> </"+str+">");
        }
        String result = String.format(SOUP_TEMPLATE,model.getTargetName(),model.getMethodName(),args,model.getMethodName());
        return result;
    }

    private static SoapModel getSoapModel(String url){
        SoapModel soap = new SoapModel();
        try {
            WSDLFactory factory = WSDLFactory.newInstance();
            WSDLReader reader = factory.newWSDLReader();
            Definition wsdlDefinition = reader.readWSDL(url);
            //解析types节点
            Map<String,Element> elementMap = parseSchemaElement(wsdlDefinition.getTypes().getExtensibilityElements());
            soap.setTargetName(wsdlDefinition.getTargetNamespace());
            List<Service> services = new ArrayList<>(wsdlDefinition.getServices().values());
            for(Service service : services){
                List<Port> ports = new ArrayList<>(service.getPorts().values());
                for(Port port : ports){
                    PortType portType = port.getBinding().getPortType();
                    List<Operation> operations = portType.getOperations();
                    for(Operation operation : operations){
                        soap.setMethodName(operation.getName());
                        soap.setArgs(parseChildElement(elementMap.get(operation.getInput().getName()),null));
                    }
                }
            }
        }catch (Exception e){
            BizLogger.error(e);
        }
        return soap;
    }

    private static Map<String,Element> parseSchemaElement(List<ExtensibilityElement> extensibilityElements) {
        Map<String,Element> result = new HashMap<>();
        for (ExtensibilityElement element : extensibilityElements) {
            if (element instanceof javax.wsdl.extensions.schema.Schema schema) {
                Element schemaElement = schema.getElement();
                NodeList childNodes = schemaElement.getChildNodes();
                for (int i = 0; i < childNodes.getLength(); i++) {
                    if (childNodes.item(i) instanceof Element) {
                        Element childElement = (Element) childNodes.item(i);
                        if(childElement.getNodeName().equals("xs:complexType")) {
                            result.put(childElement.getAttribute("name"),childElement);
                        }
                    }
                }
            }
        }
        return result;
    }

    private static List<String> parseChildElement(Element element,List<String> args) {
        if(args == null){
            args = new ArrayList<>();
        }
        if(element.getNodeName().equals("xs:element")){
            args.add(element.getAttribute("name"));
        }
        // 递归处理子节点
        NodeList childNodes = element.getChildNodes();
        for (int i = 0; i < childNodes.getLength(); i++) {
            if (childNodes.item(i) instanceof Element element1) {
                parseChildElement(element1,args);
            }
        }
        return args;
    }

    @Data
    public static class SoapModel{
        private String targetName;

        private String methodName;

        private List<String> args;
    }
}
